package org.dromara.netbar.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.NetbarConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.utils.AssertUtils;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.RedisKeyGeneratorUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.netbar.domain.NetbarCoupon;
import org.dromara.netbar.domain.bo.NetbarCouponMemberBo;
import org.dromara.netbar.domain.bo.NetbarMemberBo;
import org.dromara.netbar.domain.bo.NetbarMemberLoginBo;
import org.dromara.netbar.domain.vo.NetbarCouponMemberVo;
import org.dromara.netbar.domain.vo.NetbarCouponVo;
import org.dromara.netbar.domain.vo.NetbarMemberVo;
import org.dromara.netbar.service.INetbarCouponMemberService;
import org.dromara.netbar.service.INetbarCouponService;
import org.dromara.netbar.service.INetbarMemberBalanceLogService;
import org.dromara.netbar.service.INetbarMemberService;
import org.dromara.netbar.util.CouponUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 卡券实现类
 * @author ZhouWenTao
 * @create 2025-07-18 14:11
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class CouponService {
    private final INetbarCouponService netbarCouponService;
    private final INetbarCouponMemberService netbarCouponMemberService;
    private final INetbarMemberService netbarMemberService;

    /**
     * 用户使用卡券
     * @param bo {memberId 用户Id,couponId 卡券Id}
     * @return
     */
    public R<?> useCoupon(NetbarCouponMemberBo bo) {
        Long memberId = bo.getMemberId();
        Long couponId = bo.getCouponId();
        AssertUtils.notNull(memberId, "会员ID不能为空");
        AssertUtils.notNull(couponId, "卡券ID不能为空");
        Date nowDate = new Date();
        // 查询卡券
        String nowDateStrs = DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, nowDate);
        List<NetbarCouponMemberVo> netbarCouponMemberVos = netbarCouponMemberService.listCouponMemberGroupByMemberIds(Collections.singletonList(memberId), nowDateStrs);
        List<NetbarCouponMemberVo> list = netbarCouponMemberVos.stream().filter(item -> item.getCouponId().equals(couponId) && "0".equals(item.getStatus())).toList();
        if (list.isEmpty()) {
            return R.fail("无效卡券");
        }
        NetbarCouponMemberVo netbarCouponMemberVo = list.get(0);
        NetbarCouponVo netbarCouponVo = netbarCouponService.queryById(couponId);
        AssertUtils.notNull(netbarCouponVo, String.format("卡券不存在[%s]", NetbarConstants.COUPON_NOT_ERROR));
        // 代金券只能在柜台使用
        if (NetbarConstants.Coupon.TYPE_6.equals(netbarCouponVo.getCouponType()) && !bo.isUseDjFlag()) {
            R.fail("当前卡券仅可以在柜台使用");
        }
        /*if (!NetbarConstants.ALL_COUPON_TYPES.contains(netbarCouponVo.getCouponType())) {
            return R.fail(String.format("当前卡券不能使用[%s]", NetbarConstants.COUPON_CONDITION_ERROR));
        }*/
        String mainKey = RedisKeyGeneratorUtils.getMemberUseCouponKey(netbarCouponVo.getTenantId());
        String hKey = RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, netbarCouponVo.getCouponType());

        NetbarCouponMemberBo netbarCouponMemberBo = new NetbarCouponMemberBo();
        netbarCouponMemberBo.setCouponMemberId(netbarCouponMemberVo.getCouponMemberId());
        netbarCouponMemberBo.setUseTime(nowDate);
        netbarCouponMemberBo.setStatus("1");
        if(NetbarConstants.Coupon.TYPE_6.equals(netbarCouponVo.getCouponType())){
            // 代金券
            netbarCouponMemberService.updateByBo(netbarCouponMemberBo);
        }else if (NetbarConstants.Coupon.TYPE_1.equals(netbarCouponVo.getCouponType())) {
            Long couponAmount = netbarCouponVo.getCouponAmount();
            // 给会员加奖励余额
            netbarMemberService.addMemberAwardBalance(memberId, couponAmount);
            // 修改卡券使用状态
            netbarCouponMemberService.updateByBo(netbarCouponMemberBo);
            // TODO 写入日志
        }else if(NetbarConstants.Coupon.TYPE_2.equals(netbarCouponVo.getCouponType()) || NetbarConstants.Coupon.TYPE_3.equals(netbarCouponVo.getCouponType())){
            // 修改卡券使用状态
            netbarCouponMemberService.updateByBo(netbarCouponMemberBo);
            netbarCouponVo.setUseTime(nowDate);
            RedisUtils.setCacheMapValue(mainKey, hKey, netbarCouponVo);
            // 如果用户使用了包时券/包夜券，则修改用户状态 为待上机
            NetbarMemberVo netbarMemberVo = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getOnlineClientKey(netbarCouponVo.getTenantId()), memberId+"");
            if (null == netbarMemberVo) {
                netbarMemberService.changeStatus(new NetbarMemberBo() {{
                    setMemberId(memberId);
                    setStatus("3");
                }});
            }
            // TODO 写入日志
        }else if(NetbarConstants.Coupon.TYPE_4.equals(netbarCouponVo.getCouponType())){
            // 修改卡券使用状态
            netbarCouponMemberService.updateByBo(netbarCouponMemberBo);
            netbarCouponVo.setUseTime(nowDate);
            RedisUtils.setCacheMapValue(mainKey, hKey, netbarCouponVo);
            // TODO 写入日志
        }else if(NetbarConstants.Coupon.TYPE_5.equals(netbarCouponVo.getCouponType())){
            // 会员升级券
            Long memberLevelId = netbarCouponVo.getMemberLevelId();
            netbarMemberService.levelUpgrade(memberId, memberLevelId);
            netbarCouponMemberService.updateByBo(netbarCouponMemberBo);
        }else{
            return R.fail(String.format("当前卡券不能使用[%s]", NetbarConstants.COUPON_CONDITION_ERROR));
        }
        return R.ok("使用成功");
    }

    /**
     * 使用自动优惠券
     * @param memberId 会员ID
     * @param couponType 优惠券类型
     */
    public void useAutoCoupon(Long memberId, String couponType){
        try {
            if (NetbarConstants.Coupon.TYPE_1.equals(couponType)) {
                // 获取用户所有的自动可用代金券
                List<NetbarCouponMemberVo> couponMemberVoList = netbarCouponMemberService.listCouponMemberAuto(Arrays.asList(memberId), couponType);
                if (CollectionUtil.isNotEmpty(couponMemberVoList)) {
                    // 将自动的代金券都用掉
                    List<Long> couponIdList = couponMemberVoList.stream().map(NetbarCouponMemberVo::getCouponId).toList();
                    List<NetbarCouponVo> netbarCouponVos = netbarCouponService.queryByIds(couponIdList);
                    Optional<NetbarCouponVo> netbarCouponVoOptional = null;
                    Long totalAmount = null;
                    List<Long> usedCouponIdList = new ArrayList<>();
                    for (NetbarCouponMemberVo couponRecord : couponMemberVoList) {
                        if (0 == couponRecord.getCouponNumber() || 2 == couponRecord.getCoupon().getIsAutoUse()) continue;
                        netbarCouponVoOptional = netbarCouponVos.stream().filter(c -> c.getCouponId().equals(couponRecord.getCouponId())).findAny();
                        if (netbarCouponVoOptional.isEmpty()) {
                            continue;
                        }
                        usedCouponIdList.add(couponRecord.getCouponId());
                        totalAmount = netbarCouponVoOptional.get().getCouponAmount() * couponRecord.getCouponNumber();
                        // TODO 给用户加钱
                        netbarMemberService.addBalance(memberId, totalAmount, NetbarConstants.Member.BALANCE_TYPE_AWARD);
                        // TODO 记录使用代金券日志
                        log.info("自动使用代金券:{} * {}", couponRecord.getCoupon().getCouponName(), couponRecord.getCouponNumber());
                    }

                    if(CollectionUtil.isNotEmpty(usedCouponIdList)){
                        // 将用户的这些卡券改为已使用
                        netbarCouponMemberService.changeStatusByMemberIdListByCouponIdList(Collections.singletonList(memberId), usedCouponIdList, "1");
                    }
                }
            }
        }catch (Exception e){
            log.error("自动使用优惠券异常:{}", e.getMessage());
        }
    }

    /**
     * 获取用户卡券有效
     * @param bo {memberId 用户Id, tenantId 租户Id, clientId 客户端Id}
     * @return
     */
    public R<?> getValidCoupons(NetbarMemberLoginBo bo) {
        return R.ok(getValidCoupons(bo, true));
    }

    /**
     * 获取会员卡券列表（会检查座位和是否自动使用，排序顺序：包时券>折扣券>代金券）
     * @param bo {memberId 用户Id, tenantId 租户Id, seatTypeId 终端类型Id}
     * @param isAutoUse 是否自动使用
     * @return
     */
    public List<NetbarCouponMemberVo> getValidCoupons(NetbarMemberLoginBo bo, Boolean isAutoUse) {
        Long memberId = bo.getMemberId();
        Long seatTypeId = bo.getSeatTypeId();
//        String tenantId = bo.getTenantId();

        // 获取会员未使用未到期的卡券
        List<NetbarCouponMemberVo> couponMemberVoList = netbarCouponMemberService.listCouponMemberGroupByMemberIds(Collections.singletonList(memberId));
        if (CollectionUtil.isEmpty(couponMemberVoList)) {
            return Collections.emptyList();
        }

        long currentTimeMillis = System.currentTimeMillis();
        couponMemberVoList = couponMemberVoList.stream()
            .filter(member -> null != member.getCoupon())
            .filter(member -> {
                // 如果是包时券，需要检测包时时间是否有效
                if (NetbarConstants.Coupon.TYPE_3.equals(member.getCoupon().getCouponType()) || NetbarConstants.Coupon.TYPE_2.equals(member.getCoupon().getCouponType())) {
                    return CouponUtils.checkCouponBsValid(member.getCoupon(), currentTimeMillis);
                }
                return true; // 非包时券直接通过
            })
            .collect(Collectors.toList());

        // 修复点1：筛选自动使用的卡券
        if (isAutoUse) {
            couponMemberVoList = couponMemberVoList.stream()
                .filter(item -> (isAutoUse ? 1 : 2) == item.getCoupon().getIsAutoUse())
                .collect(Collectors.toList()); // 改用可变集合
        }

        // 修复点2：筛选座位类型
        if(null != seatTypeId){
            couponMemberVoList = couponMemberVoList.stream()
                .filter(item ->
                    (null != item.getCoupon() && (
                        (null != item.getCoupon().getValidArea() && seatTypeId.equals(item.getCoupon().getValidArea())) ||
                            (null == item.getCoupon().getValidArea() || 0L == item.getCoupon().getValidArea())))
                )
                .collect(Collectors.toList()); // 改用可变集合
        }

        // 排序：优先包时券（有座位类型>0>null），再是折扣券（有座位类型>0>null），最后是代金券
        couponMemberVoList.sort((o1, o2) -> {
            NetbarCouponVo coupon1 = o1.getCoupon();
            NetbarCouponVo coupon2 = o2.getCoupon();

            // 获取券类型优先级（假设：1-包时券，2-折扣券，3-代金券）
            int type1Priority = getCouponTypePriority(coupon1.getCouponType());
            int type2Priority = getCouponTypePriority(coupon2.getCouponType());

            // 先按券类型排序
            if (type1Priority != type2Priority) {
                return Integer.compare(type1Priority, type2Priority);
            }

            // 同类型券按座位类型优先级排序
            int seat1Priority = getSeatTypePriority(coupon1.getValidArea(), seatTypeId);
            int seat2Priority = getSeatTypePriority(coupon2.getValidArea(), seatTypeId);

            return Integer.compare(seat1Priority, seat2Priority);
        });

        return couponMemberVoList;
    }

    /**
     * 券列表转成Map<券类型_券适用区域, 券对象>
     * @param couponMemberVoList 券列表
     * @return Map<券类型_券适用区域, 券对象>
     */
    public static Map<String, NetbarCouponMemberVo> getValidCouonsToSeatTypeNameMap(List<NetbarCouponMemberVo> couponMemberVoList){
        Map<String, NetbarCouponMemberVo> map = new LinkedHashMap<>();
        for (NetbarCouponMemberVo netbarCouponMemberVo : couponMemberVoList) {
            map.put(netbarCouponMemberVo.getCoupon().getCouponType() + "_" + netbarCouponMemberVo.getCoupon().getValidArea(), netbarCouponMemberVo);
        }
        return map;
    }
    /**
     * 获取券类型优先级
     * @param couponType 券类型
     * @return 优先级数字，越小优先级越高
     */
    private static int getCouponTypePriority(String couponType) {
        if (couponType == null) return 999;

        return switch (couponType) {
            case NetbarConstants.Coupon.TYPE_3 -> 1; // 包夜券
            case NetbarConstants.Coupon.TYPE_2 -> 2; // 包时券
            case NetbarConstants.Coupon.TYPE_4 -> 3; // 折扣券
            default -> 999;
        };
    }

    /**
     * 获取座位类型优先级
     * @param validArea 券的适用区域
     * @param currentSeatTypeId 当前座位类型ID
     * @return 优先级数字，越小优先级越高
     */
    private static int getSeatTypePriority(Long validArea, Long currentSeatTypeId) {
        if (validArea != null && validArea.equals(currentSeatTypeId)) {
            return 1; // 匹配当前座位类型，最高优先级
        } else if (validArea != null && validArea == 0L) {
            return 2; // 通用券（0），中等优先级
        } else if (validArea == null) {
            return 3; // null，最低优先级
        } else {
            return 999; // 其他情况
        }
    }

    public void sendCoupon(Map<Long, Long> couponIdMap, List<Long> memberIds) {
        if (null == couponIdMap || couponIdMap.isEmpty() || CollectionUtil.isEmpty(memberIds)){
            return;
        }
        // 保存卡券
        netbarCouponMemberService.saveCouponMemberByMap(couponIdMap, memberIds);
        try {
            // 获取用户的自动代金券
            List<NetbarCouponMemberVo> couponMemberVoList = netbarCouponMemberService.listCouponMemberAuto(memberIds, NetbarConstants.Coupon.TYPE_1);
            NetbarCouponMemberBo couponMemberBo = null;
            // 使用
            for (Long memberId : memberIds) {
                for (NetbarCouponMemberVo netbarCouponMemberVo : couponMemberVoList) {
                    couponMemberBo = new NetbarCouponMemberBo();
                    for (long l = 0L; l < netbarCouponMemberVo.getCouponNumber(); l++) {
                        couponMemberBo.setCouponId(netbarCouponMemberVo.getCouponId());
                        couponMemberBo.setMemberId(memberId);
                        useCoupon(couponMemberBo);
                    }
                }
            }
        }catch (Exception e){
            log.error("会员发放卡券后自动使用异常", e);
        }
    }
}

